<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>瀑布流</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        background-color: #eee;
      }

      .container {
        margin: 0 auto;
        height: auto;
        position: relative;
      }

      .item {
        border-radius: 16px;
        position: absolute;
        overflow: hidden;
        box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
      }

      .item img {
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div class="container">
    </div>
    <script>
      class WaterfallFlow {
        #defaultCount = 200
        #columnWidth = 200
        #gapWidth = 8
        #gapHeight = 8
        #container = null
        #columnCount = 0
        #columnHeights = []
        #loading = false
        #isSupplementWidth = true
      
        constructor(container, { columnWidth, gapWidth, gapHeight, isSupplementWidth } = {}) {
          this.#columnWidth ||= columnWidth
          this.#gapWidth ||= gapWidth
          this.#gapHeight ||= gapHeight
          this.#container ||= container
          this.#isSupplementWidth ||= isSupplementWidth
        }
        
        init() {
          this.#resetColumnCount()
          this.#resetColumnHeights()
          this.#setUpCells()
          this.#bindEvent()
        }
        
        #bindEvent() {
          window.addEventListener('scroll', () => {
            const viewPortBottom = document.body.clientHeight
            const currentTop = document.documentElement.scrollTop + document.documentElement.clientHeight
            if (!this.#loading  && viewPortBottom - currentTop <= 500) {
              this.#setUpCells()
            }
          })
        }
        
        async #setUpCells() {
          this.#loading = true
          const cells = await this.#loadCells()
          setTimeout(() => {
            this.#adjustCells(cells)
          }, 10)
          this.#loading = false
        }
        
        async #loadCells() {
          const fragment = document.createDocumentFragment() 
          const cells = []
          const datas = await Promise.all(new Array(this.#columnCount * 2).fill(0).map(() => this.#fetchData()))
          datas.forEach(blob => {
            const cell = document.createElement('div')
            cell.classList.add('item')
            cell.style.width = this.#columnWidth + 'px'
            cell.innerHTML = `<img src="${URL.createObjectURL(blob)}" alt="" />`
            fragment.appendChild(cell)
            cells.push(cell)
          })
          // for (var i = 0; i < this.#columnCount * 2; i++) {
          //   const blob = await this.#fetchData()
          //   const cell = document.createElement('div')
          //   cell.classList.add('item')
          //   cell.style.width = this.#columnWidth + 'px'
          //   cell.innerHTML = `<img src="${URL.createObjectURL(blob)}" alt="" />`
          //   fragment.appendChild(cell)
          //   cells.push(cell)
          // }
          this.#container.appendChild(fragment)
          return cells
        }
        
        #adjustCells(cells) {
          cells.forEach(cell => {
            // 找出最小高度
            const minHeight = Math.min(...this.#columnHeights)
            // 找出最小高度的索引
            const minHeightIndex = this.#columnHeights.indexOf(minHeight)
            // 设置left =  最小高度的索引 * 宽度 + 间距
            cell.style.left = minHeightIndex * (this.#columnWidth + this.#gapWidth) + 'px'
            // 设置top = 最小高度 + 间距高度
            const top = minHeight + this.#gapHeight
            cell.style.top = top + 'px'
            // 保存本索引占用的高度
            this.#columnHeights[minHeightIndex] = top + cell.clientHeight 
          })
          this.#container.style.height = Math.max(...this.#columnHeights) + 'px'
        }
        
        #resetColumnCount() {
          this.#columnCount = Math.floor(document.body.clientWidth / (this.#columnWidth + this.#gapWidth))
          this.#supplementWidth()
        }
        
        #supplementWidth() {
          if (this.#isSupplementWidth && document.body.clientWidth - this.#columnCount * this.#columnWidth + this.#gapWidth >= this.#gapWidth) {
            this.#columnWidth = (document.body.clientWidth - this.#gapWidth * (this.#columnCount + 1)) / this.#columnCount
          }
        }
        
        #resetColumnHeights() {
          this.#columnHeights = new Array(this.#columnCount).fill(0)
        }
        
        async #fetchData() {
          return fetch(`https://globe.cdn.media-library.fun/Image/Phone/image-phone-${this.#getNumber()}.webp`)
            .then(res => res.blob())
        }
        
        #getNumber() {
          return Math.floor(Math.random() * 1000 + 100)
        }
      }
      const container = document.querySelector('.container')
      const waterfallFlow =  new WaterfallFlow(container)

      waterfallFlow.init()
    </script>
  </body>
</html>